<?php

namespace App\Services\MakeProductOrder;

use App\Models\User;
use App\Models\Address;
use App\Models\ProductSku;
use App\Models\GoldRecord;
use App\Models\SeckillProduct;
use Brick\Math\RoundingMode;
use App\Events\OrderCreatedEvent;
use Illuminate\Support\Facades\DB;
use Illuminate\Database\Eloquent\Model;
use Rainsens\Dinglog\Facades\Dinglog;
use Illuminate\Support\Facades\Redis;
use Carbon\Carbon;

class MakeProductOrder
{
	protected ?User $user = null;

	protected ?Address $address = null;

	protected ?Model $order = null;

	/**
	 * sku_id, attr_value_index, amount.
	 */
	protected array $items = [];

	protected ?GoldRecord $goldRecord = null;

	public function __construct(
		User $user,
		Address $address,
		array $items = [],
		$sid = 0
		)
	{
		$this->user = $user;
		$this->address = $address;
		$this->items = $items;
		$this->sid = $sid;
	}

	protected function create()
	{
		DB::transaction(function () {
			if($this->sid > 0){

				Redis::decr(SeckillProduct::seckillProductNumberCacheKey($this->sid));
				SeckillProduct::find($this->sid)->decrement('seckill_count');
			} 
			$this->order = $this->user->orders()->create(['sid'=>$this->sid]);
			
			$this->addAddressToOrder();
			$this->addItemsToOrder();
			
			event(new OrderCreatedEvent($this->order));
			$this->order->unsetRelations();
		});

		return $this->order;
	}

	protected function addAddressToOrder()
	{
		$order = $this->order;
		$address = $this->address;
		$order->province = $address->province;
		$order->city = $address->city;
		$order->district = $address->district;
		$order->address = [
			'address' => $address->full_address,
			'contact_name' => $address->contact_name,
			'contact_phone' => $address->contact_phone
		];
	}

	protected function addItemsToOrder()
	{
		$items = $this->items;
		$orderTotalMoney = big_decimal(0);

		foreach ($items as $item) {
			$itemTotalMoney = $this->createOrderItem($item);

			$orderTotalMoney = $orderTotalMoney->plus($itemTotalMoney);
		}

		$this->updateOrderAfterAddingItem($orderTotalMoney);
	}

	protected function createOrderItem($item)
	{
		$amount = $item['amount'];
		$sku = isset($item['sku_id']) ? ProductSku::findOrFail($item['sku_id']) : sku_from_attr($item['attr_value_index']);
		
		if ($amount > $sku->stock) fail('商品库存不足，无法创建订单');

		$itemTotalMoney = $this->getItemTotalMoney($sku, $amount);

		$this->order->items()->create([
			'vendor_id' => $sku->product->vendor_id,
			'product_id' => $sku->product_id,
			'product_sku_id' => $sku->id,
			'product_title' => $sku->product->title,
			'product_cover' => $sku->product->cover,
			'sku_name' => $sku->name,
			'sku_pic' => $sku->pic,
			'sku_attr_value_index' => $sku->attr_value_index,
			'sku_attr_value' => $sku->getAttrKeyValue(),
			'amount' => $amount,
			'cost' => $this->getItemSkuCost($sku),
			'total_cost' => $this->getItemSkuTotalCost($sku, $amount),
			'price' => $this->getFinalSkuPrice($sku),
			'total_amount' => $itemTotalMoney,
			'profit' => $this->getItemSkuProfit($sku),
			'total_profit' => $this->getItemSkuTotalProfit($sku, $amount),
		]);

		if (! $sku->decreaseStock($item['amount'])) fail('商品库存不足，无法创建订单');

		return $itemTotalMoney;
	}

	protected function updateOrderAfterAddingItem($totalMoney)
	{
		$this->order->update([
			'total_amount' => (string)$totalMoney,
			'total_profit' => (string)$this->getOrderProfit(),
			'is_gold_bean' => $this->order->isGoldBeanOrder(),
			'is_gold_coin' => $this->order->isGoldStormOrder(),
			'is_integral'  => $this->order->isIntegralOrder()
		]);
	}

	protected function getFinalSkuPrice(ProductSku $sku)
	{
		if ($this->user->isValidVip()) {
			return big_decimal($sku->price_vip);
		} else {
			return big_decimal($sku->price);
		}
	}

	protected function getItemSkuCost(ProductSku $sku)
	{
		return big_decimal($sku->price_cost);
	}

	protected function getItemSkuTotalCost(ProductSku $sku, $amount)
	{
		$skuCostPrice = big_decimal($sku->price_cost);
		return $skuCostPrice->multipliedBy($amount)->toScale(2, RoundingMode::HALF_UP);
	}

	protected function getItemSkuProfit(ProductSku $sku)
	{
	
		$skuPrice = $this->getFinalSkuPrice($sku);
		$skuCostPrice = $this->getItemSkuCost($sku);
		return $skuPrice->minus($skuCostPrice);
		
	}

	protected function getItemSkuTotalProfit(ProductSku $sku, $amount)
	{
		$profit = $this->getItemSkuProfit($sku);
		return $profit->multipliedBy($amount)->toScale(2, RoundingMode::HALF_UP);
	}

	protected function getItemTotalMoney(ProductSku $sku, $amount)
	{
		$skuPrice = $this->getFinalSkuPrice($sku);
		return $skuPrice->multipliedBy($amount)->toScale(2, RoundingMode::HALF_UP);
	}

	protected function getOrderProfit()
	{
	
		$order = $this->order;
		$total = big_decimal($order->items()->sum('total_amount'));
		$cost = $order->items()->sum('total_cost');
		return $total->minus($cost)->toScale(2, RoundingMode::HALF_UP);
		
	}

	public function make()
	{
		return $this->create();
	}
}
